﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace Utility.DataStructure.Octree
{
	public class OctreeNode<T>
	{
		public static OctreeNode<T> NewRoot ()
		{
			var node = new OctreeNode<T> ();
			return node;
		}

		public int OctantId { get; private set; } = -1;
		public Vector3 OriginCoord { get; private set; } = Vector3.zero;

		public OctreeNode<T> Parent { get; private set; }
		public int Depth { get; private set; } = 0;//depth in tree
		public int Divisor { get { return 1 << Depth; } }

		OctreeNode<T>[] childs;
		public IEnumerable<OctreeNode<T>> Childs { get { return childs; } }

		public T Data { get; set; }

		public bool IsLeaf
		{
			get { return childs == null; }
		}

		public OctreeNode<T> this[int octant]
		{
			get { return childs?[octant]; }
		}

		public OctreeNode<T> GetChild (int octant)
		{
			return childs?[octant];
		}

		public OctreeNode<T> GetChild (bool x, bool y, bool z)
		{
			return childs?[OctreeUtility.Signs_to_Octant (x, y, z)];
		}

		//ctor
		OctreeNode ()
		{
		}

		public void Divide ()
		{
			childs = new OctreeNode<T>[8];
			for (int i = 0; i < 8; i++)
			{
				var child = new OctreeNode<T> ();
				child.OctantId = i;
				child.Parent = this;
				child.Depth = Depth + 1;

				var octantCoord = OctreeUtility.GetOctantOriginCoord (i);
				child.OriginCoord = OriginCoord + octantCoord / Divisor;

				childs[i] = child;
			}
		}

		public void ClearChilds ()
		{
			childs = null;
		}

		public T GetLeafData (Vector3 coord, out int depth)
		{
			if (IsLeaf)
			{
				depth = Depth;
				return Data;
			}

			int octant = OctreeUtility.GetOctant (coord);
			var octantNode = childs[octant];
			Vector3 octantCoord = OctreeUtility.GetOctantCoord (coord);

			return octantNode.GetLeafData (octantCoord, out depth);
		}

		public NodeInfo<T> GetInfo ()
		{
			return new NodeInfo<T> ()
			{
				data = Data,
				origin = OriginCoord,
				depth = Depth
			};
		}
	}

	public struct NodeInfo<T>
	{
		public T data;
		public Vector3 origin;
		public int depth;
	}
}